﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Common.Utilities;
using Common.Utilities.TextMatch;

namespace Workflows.Components.Entities.Distances.FieldDistances
{
    public class PersonFirstNameComparer : IAttributeDistanceCalcutor
    {
        private int MinLenWhenSubstringMatch = 3;

        public PersonFirstNameComparer()
        {
            this._ComparerSettings = new Dictionary<string, string>();

            this._ComparerSettings.Add("UnevenPersonNameConnectionString", "");
            this._ComparerSettings.Add("UnevenPersonNameTableName", "");
            this._ComparerSettings.Add("UnevenPersonNameFieldName", "");
        }

        #region uneven distribution
        private string _UnevenDistributionTableName;
        /// <summary></summary>
        public string UnevenDistributionTableName
        {
            get
            {
                return this._UnevenDistributionTableName;
            }
            set
            {
                this._UnevenDistributionTableName = value;
            }
        }

        private string _UnevenDistributionFieldName;
        /// <summary></summary>
        public string UnevenDistributionFieldName
        {
            get
            {
                return this._UnevenDistributionFieldName;
            }
            set
            {
                this._UnevenDistributionFieldName = value;
            }
        }

        private string _UnevenDistributionConnStr;
        /// <summary></summary>
        public string UnevenDistributionConnStr
        {
            get
            {
                return this._UnevenDistributionConnStr;
            }
            set
            {
                this._UnevenDistributionConnStr = value;
            }
        }

        private bool _UnevenlyDistributed = true;
        /// <summary></summary>
        public bool UnevenlyDistributed
        {
            get
            {
                return this._UnevenlyDistributed;
            }
            set
            {
                this._UnevenlyDistributed = value;
            }
        }

        private Dictionary<string, double> _UnevenDistributionAdjustment;
        #endregion
        
        /// <summary>
        /// uppercase name -> degree of adjustment [1,+)
        /// adjusted score = score / adjustment
        /// </summary>
        public Dictionary<string,double> UnevenDistributionAdjustment
        {
            get
            {
                return this._UnevenDistributionAdjustment;
            }
            set
            {
                this._UnevenDistributionAdjustment = value;
            }
        }

        #region IAttributeComparer Members

        /// <summary>
        /// load dictionary into memory or read config settings from file
        /// </summary>
        public void InitInMemorySettings()
        {
            this._UnevenDistributionAdjustment=new Dictionary<string, double>();
            if(this.UnevenlyDistributed && 
                !string.IsNullOrEmpty(this._UnevenDistributionConnStr) && 
                !string.IsNullOrEmpty(this._UnevenDistributionTableName) && 
                !string.IsNullOrEmpty(this._UnevenDistributionFieldName))
            {
                SqlConnection conn=new SqlConnection(this._UnevenDistributionConnStr);
                try
                {
                    conn.Open();
                    SqlCommand cmd = conn.CreateCommand();
                    int totalCount = 0;
                    int distinctCount = 0;
                    int cutoff = 0;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = string.Format("select count ({0}) from {1} where {0} is not null",
                                                    this._UnevenDistributionFieldName, this._UnevenDistributionTableName);
                    totalCount = int.Parse(cmd.ExecuteScalar().ToString());
                    cmd.CommandText = string.Format("select count (distinct {0}) from {1} where {0} is not null",
                                                    this._UnevenDistributionFieldName, this._UnevenDistributionTableName);
                    distinctCount = int.Parse(cmd.ExecuteScalar().ToString());
                    if(distinctCount>0 && totalCount>distinctCount)
                    {
                        cutoff = (int) ((double) totalCount/distinctCount);
                        cmd.CommandText =
                            string.Format(
                                "select {0}, Log(cast(count({0}) as float)/{2}) as Adjustment " +
                                "from {1} where {0} is not null group by {0} " +
                                "having count ({0})>={2}",
                                this._UnevenDistributionFieldName,
                                this._UnevenDistributionTableName,
                                cutoff);
                        SqlDataReader reader = cmd.ExecuteReader();
                        while(reader.Read())
                        {
                            string name = reader[0].ToString();
                            double adjustment = Math.Max(1.0, double.Parse(reader[1].ToString()));
                            this._UnevenDistributionAdjustment.Add(name,adjustment);
                        }
                        reader.Close();
                    }
                }
                catch (Exception ex)
                {
                    ExceptionHandler.HandlesException(ex);
                }
                finally
                {
                    conn.Close();
                }
            }
        }

        public double Compare(object attrValue1, object attrValue2, ref MatchResult result)
        {
            result = MatchResult.NullMatch;
            if (attrValue1 == null || attrValue2 == null)
                return 0;

            string strVal1 = attrValue1.ToString();
            string strVal2 = attrValue2.ToString();
            if (strVal1.Length == 0 || strVal2.Length == 0)
                return 0;

            try
            {
                if (strVal1.ToLower() == strVal2.ToLower())
                {
                    result = MatchResult.PerfectMatch;
                    return 1.00;
                }
                else if (strVal1.Length == 1 || strVal2.Length == 1)
                {
                    if (strVal1.Substring(0, 1).ToLower() == strVal2.Substring(0, 1).ToLower())
                    {
                        result = MatchResult.PositiveMatch;
                        return 0.75;
                    }
                }
                if (strVal1.IndexOf(",") > 0)
                    strVal1 = strVal1.Substring(strVal1.IndexOf(",") + 1);
                if (strVal1.IndexOf(" ") > 0)
                    strVal1 = strVal1.Substring(0, strVal1.IndexOf(" "));
                if (strVal2.IndexOf(",") > 0)
                    strVal2 = strVal2.Substring(strVal2.IndexOf(",") + 1);
                if (strVal2.IndexOf(" ") > 0)
                    strVal2 = strVal2.Substring(0, strVal2.IndexOf(" "));
                if (strVal1.ToLower() == strVal2.ToLower() && strVal2.Length > 0)
                {
                    result = MatchResult.PositiveMatch;
                    return 0.95;
                }

                if (StringUtil.LongestCommonSubsequence(strVal1, strVal2) >= MinLenWhenSubstringMatch)
                {
                    int commonStrLen = StringUtil.LongestCommonSubsequence(strVal1, strVal2);
                    result = MatchResult.PositiveMatch;
                    return (double) commonStrLen/Math.Max(strVal1.Length, strVal2.Length);
                }

                //else if (this.GetSoundexEncoding(strVal1) == this.GetSoundexEncoding(strVal2))
                //{
                //    result = MatchResult.PositiveMatch;
                //    return 0.50;
                //}
                //else if (this.GetMetaphoneEncoding(strVal1) == this.GetMetaphoneEncoding(strVal2))
                //{
                //    result = MatchResult.PositiveMatch;
                //    return 0.35;
                //}

                result = MatchResult.NegativeMatch;
                return 0;
            }
            catch (Exception ex)
            {
                ExceptionHandler.HandlesException(ex);
                return 0.0;
            }
        }

        public double Compare(Entity entity1, Entity entity2, AttributeMetaData attrMetaData, ref MatchResult result)
        {
            
            Debug.Assert(!string.IsNullOrEmpty(attrMetaData.AttributeName));

            string attrName = attrMetaData.AttributeName;
            object attrValue1 = null;
            object attrValue2 = null;
            if (entity1.ComparableAttributes.ContainsKey(attrName))
                attrValue1 = entity1.ComparableAttributes[attrName];
            if (entity2.ComparableAttributes.ContainsKey(attrName))
                attrValue2 = entity2.ComparableAttributes[attrName];
            return this.Compare(attrValue1, attrValue2, ref result);
        }

        public string[] ArgumentNames
        {
            get
            {
                return new string[] {"FirstName"};
            }
        }

        private Dictionary<string, string> _ComparerSettings;
        public Dictionary<string, string> ComparerSettings
        {
            get
            {
                return _ComparerSettings;
            }
            set
            {
                this._ComparerSettings = value;
            }
        }

        #endregion

        #region phonetic
        /// <summary>
        /// soundex encoding of string
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string GetSoundexEncoding(string value)
        {
            SoundexConverter converter = new SoundexConverter();
            return converter.Convert(value);
        }

        /// <summary>
        /// metaphone encoding
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public string GetMetaphoneEncoding(string value)
        {
            MetaphoneConverter converter = new MetaphoneConverter();
            return converter.Convert(value);
        }
        #endregion
    }
}
